1 module hip.util.to_string_range;
2 import std.range.interfaces;
3 import std.range.primitives;
4 import std.traits:isArray;
5 import std.typecons:isTuple;
6 
7 void put(Sink, E)(ref Sink sink, E e)
8 {
9     static if(is(E == U[], U))
10     {
11         static if(__traits(hasMember, sink, "preAllocate"))
12             sink.preAllocate(e.length);
13         foreach(element; e)
14             sink.put(element);
15     }
16     else
17         sink.put(e);
18 }
19 
20 
21 void toStringRange(Sink, Enum)(ref Sink sink, Enum enumMember) if(is(Enum == enum))
22 {
23     foreach(mem; __traits(allMembers, Enum))
24         if(__traits(getMember, Enum, mem) == enumMember)
25         {
26             put(sink, Enum.stringof ~ "." ~ mem); //@nogc string, resolved at compile time
27             return;
28         }
29     put(sink, Enum.stringof ~ ".|MEMBER_NOT_FOUND|"); //@nogc string, resolved at compile time
30 }
31 
32 void toStringRange(Sink)(ref Sink sink, float f)
33 if(isOutputRange!(Sink, char))
34 {
35     if(f != f) //nan
36         return put(sink, "nan");
37     else if(f == -float.infinity)
38         return put(sink, "-inf");
39     if(f == float.infinity)
40         return put(sink, "inf");
41     if(f < 0)
42     {
43         f = -f;
44         put(sink, '-');
45     }
46     
47     float decimal = f - cast(int)f;
48     toStringRange(sink, cast(int)f);
49     if(decimal == 0)
50         return;
51     put(sink, '.');
52     long multiplier = 10;
53     while(cast(long)(decimal*multiplier) < (decimal*multiplier))
54     {
55         if(cast(long)(decimal*multiplier) == 0)
56             put(sink, '0');
57         multiplier*=10;
58     }
59     toStringRange(sink, (cast(long)(decimal*multiplier)));
60 }
61 
62 
63 void toStringRange(Sink, T)(auto ref Sink sink, T[] arr)
64 if(isOutputRange!(Sink, char) && !is(T[] == string) && !is(T[] == char[])) //There is a better match for char/string
65 {
66     static if(__traits(compiles, sink.preAllocate))
67     {
68         //2: '[' and ']'
69         // 2 * arr.length: ", " (no need to use - 1 as there will be the inputs)
70         sink.preAllocate(2 + 2 * arr.length);
71     }
72     put(sink, '[');
73     for(int i = 0; i < arr.length; i++)
74     {
75         if(i != 0)
76         {
77             foreach(character; ", ")
78                 put(sink, character);
79         }
80         toStringRange(sink, arr[i]);
81     }
82     put(sink, ']');
83 }
84 
85 
86 void toStringRange(Sink, T)(auto ref Sink sink, T structOrTupleOrClass) 
87 if(!isArray!T && (is(T == struct) || is(T == class) || is(T == interface) || isTuple!T))
88 {
89     static if(isTuple!T)
90     {
91         alias tupl = structOrTupleOrClass;
92         put(sink, T.stringof);
93         put(sink, '(');
94         foreach(i, v; tupl)
95         {
96             if(i > 0)
97                 put(sink, ", ");
98             toStringRange(sink, v);
99         }
100         put(sink, ')');
101     }
102     else static if(is(T == struct))//For structs declaration
103     {
104         import hip.util.reflection;
105         alias struct_ = structOrTupleOrClass;
106         static if(__traits(hasMember, T, "toString") && hasUDA!(__traits(getMember, T, "toString"), "format"))
107         {
108             import hip.util.format;
109             formatFromType(sink, struct_);
110         }
111         else
112         {
113             put(sink, T.stringof);
114             put(sink, '(');
115             foreach(i, v; struct_.tupleof)
116             {
117                 if(i > 0)
118                     put(sink, ", ");
119                 toStringRange(sink, v);
120             }
121             put(sink, ')');
122         }
123     }
124     else static if(is(T == class))
125     {
126         alias class_ = structOrTupleOrClass;
127         put(sink, T.classinfo.name);
128         put(sink, '(');
129         foreach(i, v; class_.tupleof)
130         {
131             if(i > 0)
132                 put(sink, ", ");
133             toStringRange(sink, v);
134         }
135         put(sink, ')');
136     }
137     else static if(is(T == interface))
138     {
139         put(sink, T.classinfo.name);
140     }
141     else static assert(0, "Not implemented for "~T.stringof);
142 }
143 
144 // void toStringRange(Sink)(auto ref Sink sink, scope const char[] arr) if(isOutputRange!(Sink, char))
145 // {
146 //     static if(__traits(compiles, sink.preAllocate))
147 //         sink.preAllocate(arr.length);
148 //     foreach(ch; arr)
149 //         put(sink, ch);
150 // }
151 
152 void   toStringRange(Sink)(auto ref Sink sink, string str) if(isOutputRange!(Sink, char))
153 {
154     static if(__traits(compiles, sink.preAllocate))
155         sink.preAllocate(str.length);
156     foreach(character; str)
157         put(sink, character);
158 }
159 
160 void   toStringRange(Sink)(auto ref Sink sink, const(char)* str) if(isOutputRange!(Sink, char))
161 {
162     import core.stdc.string:strlen;
163     size_t length = strlen(str);
164     static if(__traits(compiles, sink.preAllocate))
165         sink.preAllocate(length);
166     for(int i = 0; i < length; i++)
167         put(sink, str[i]);
168 }
169 
170 void   toStringRange(Sink)(auto ref Sink sink, const(ubyte)* str) if(isOutputRange!(Sink, char))
171 {
172     toStringRange(sink, cast(const(char)*)str);
173 }
174 
175 void toStringRange(Sink)(auto ref Sink sink, void* ptr) if(isOutputRange!(Sink, char))
176 {
177     if(ptr is null)
178         put(sink, "null");
179     else
180         toHex(sink, cast(size_t)ptr);
181 }
182 
183 void toStringRange(Sink)(auto ref Sink sink, bool b) if(isOutputRange!(Sink, char))
184 {
185     put(sink, b ? "true" :"false");
186 }
187 
188 void toStringRange(Sink)(auto ref Sink sink, long x) 
189 if(isOutputRange!(Sink, char))
190 {
191     enum numbers = "0123456789";
192     int preAllocSize = 0;
193     bool isNegative = x < 0;
194     if(isNegative)
195     {
196         x*= -1;
197         preAllocSize++;
198     }
199     ulong div = 10;
200     while(div <= x)
201     {
202         div*=10;
203         preAllocSize++;
204     }
205     div/= 10;
206     static if(__traits(hasMember, sink, "preAllocate"))
207         sink.preAllocate(preAllocSize);
208     if(isNegative)
209         put(sink, '-');
210     while(div >= 10)
211     {
212         put(sink, numbers[(x/div)%10]);
213         div/=10;
214     }
215     put(sink, numbers[cast(size_t)(x%10)]);
216 }
217 
218 
219 void toHex(Sink)(auto ref Sink sink, size_t n)
220 if(isOutputRange!(Sink, char))
221 {
222     enum numbers = "0123456789ABCDEF";
223     int preAllocSize = 1;
224     ulong div = 16;
225     while(div <= n)
226     {
227         div*= 16;
228         preAllocSize++;
229     }
230     div/= 16;
231     static if(__traits(hasMember, sink, "preAllocate"))
232         sink.preAllocate(preAllocSize);
233 
234     while(div >= 16)
235     {
236         put(sink, numbers[(n/div)%16]);
237         div/= 16;
238     }
239     put(sink, numbers[n%16]);
240 }